package com.ruoyi.optimization.common;

import java.util.*;

// 遗传算法个体（代表一个解决方案）
public class Individual implements Cloneable { // 实现 Cloneable 接口
    private Map<Integer, List<Material>> materialMap; // 原料类型到实例列表的映射
    private Integer[] partsUsed; // 零件剩余计数器
    private double fitness; // 适应度值（0-1）
    private boolean isValid; // 标记是否为有效解
    private Map<Integer, Integer> partLengthToType; // 零件长度到类型的映射
    private Map<Integer, Integer> materialLengthIndex; // 原料类型到索引的映射
    private Map<Integer, Integer> partsLengthIndex; // 零件类型到索引的映射
    private List<MaterialReq> materials;
    private Map<Integer, Integer> partCounts; // 长度：数量
    private Map<Integer, Integer> materialCounts; // 长度：数量

    // 零件配置（长度和数量）
    private List<PartsReq> parts;

    public Individual() {
    }

    public Individual(List<MaterialReq> materials,List<PartsReq> parts) {
        this.materials = materials;
        this.parts = parts;

        materialMap = new HashMap<>();
        partsUsed = new Integer[parts.size()];
        fitness = 0;
        isValid = true;
        partLengthToType = new HashMap<>();
        materialLengthIndex = new HashMap<>();
        partsLengthIndex = new HashMap<>();
        partCounts = new HashMap<>();
        materialCounts = new HashMap<>();

        for (int i = 0; i < materials.size(); i++) {
            materialCounts.put(materials.get(i).getLength(), materials.get(i).getCount());
        }
        for (int i = 0; i < parts.size(); i++) {
            partCounts.put(parts.get(i).getLength(), parts.get(i).getCount());
        }

        for (int i = 0; i < parts.size(); i++) {
            partLengthToType.put(parts.get(i).getLength(), i);
        }
        for (int i = 0; i < materials.size(); i++) {
            materialMap.put(i, new ArrayList<>());
            materialLengthIndex.put(materials.get(i).getLength(), i);
        }
        for (int i = 0; i < parts.size(); i++) {
            partsLengthIndex.put(parts.get(i).getLength(), i);
        }
        backtrackInitialize(); // 使用回溯生成合法初始解
    }

    // 基于回溯的严格初始化（确保数量约束）
    private void backtrackInitialize() {
        // 生成随机零件队列（类型索引数组）
        List<Integer> partQueue = new ArrayList<>();
        for (int i = 0; i < parts.size(); i++) {
            for (int j = 0; j < parts.get(i).getCount(); j++) {
                partQueue.add(i);
            }
        }
        Collections.shuffle(partQueue);
        int num = 0;
        Map<Integer, Integer> mapNum = new HashMap<>();
        for (PartsReq dd : parts) {
            mapNum.put(dd.getLength(), 0);
        }
        // 递归回溯函数
        class Backtrack {
            boolean backtrack(int index) {
                if (index == partQueue.size()) return true; // 终止条件
                int partType = partQueue.get(index);
                int partLen = parts.get(partType).getLength();
                mapNum.put(partLen, mapNum.get(partLen) + 1);
                if (mapNum.get(partLen) > FinalNum.RETRY_COUNT) {
                    return false;
                }

                // 策略1：尝试放入现有原料实例
                List<List<Material>> values = new ArrayList<>();
                values.addAll(materialMap.values());
                for (int i = 0; i < values.size(); i++) {
                    List<Material> instances = values.get(i);
                    for (int j = 0; j < instances.size(); j++) {
                        Material inst = instances.get(j);
                        if (inst.getRemaining() >= partLen) {
                            inst.getParts().add(partLen);
                            inst.setRemaining(inst.getRemaining()-partLen);
                            if (backtrack(index + 1)) return true; // 递归
                            inst.getParts().remove(inst.getParts().size() - 1); // 回溯
                            inst.setRemaining(inst.getRemaining()+partLen);
                        }
                    }

                }
                List<MaterialReq> tmp = new ArrayList<>(materials);
                Collections.shuffle(tmp);
                // 策略2：创建新原料实例
                for (MaterialReq data : tmp) {
                    int matIdx = materialLengthIndex.get(data.getLength());
                    List<Material> instances = materialMap.get(matIdx);
                    if (instances.size() < data.getCount()) {
                        Material newInst = new Material(data.getLength(), 1);
                        if (newInst.addPart(partLen)) {
                            instances.add(newInst);
                            if (backtrack(index + 1)) return true;
                            instances.remove(instances.size() - 1); // 回溯
                        }
                    }
                }
                return false; // 无可行方案
            }
        }
        Backtrack backtrack = new Backtrack();
        // 启动回溯搜索
        if (!backtrack.backtrack(0)) {
            isValid = false;
            num++;
        }
        Arrays.fill(partsUsed, 0); // 标记全部零件已分配
    }

    // 计算适应度并验证方案合法性
    public void calculateFitness() {
        // 验证零件数量正确性
        HashMap<Integer, Integer> partCountsMap = new HashMap<>();
        for (List<Material> instances : materialMap.values()) {
            for (Material inst : instances) {
                for (Integer p : inst.getParts()) {
                    if (partCountsMap.containsKey(p)){
                        partCountsMap.put(p, partCountsMap.get(p) +1);
                    }else {
                        partCountsMap.put(p, 1);
                    }
                }
            }
        }
        for (int i = 0; i < parts.size(); i++) {
            if (partCountsMap.get(parts.get(i).getLength()) != parts.get(i).getCount()){
                isValid = false;
                break;
            }
        }

//        // 验证零件数量正确性
//        int[] partCounts = new int[parts.size()];
//        for (List<Material> instances : materialMap.values()) {
//            for (Material inst : instances) {
//                for (int p : inst.getParts()) {
//                    int type = partLengthToType.get(p);
//                    partCounts[type]++;
//                }
//            }
//        }
//
//        // 非法解处理（适应度归零）
//        boolean isValid = true;
//        for (int i = 0; i < partCounts.length; i++) {
//            if (partCounts[i] != parts.get(i).getCount()) {
//                isValid = false;
//                break;
//            }
//        }
        if (!isValid) {
            fitness = 0;
            return;
        }

        // 计算利用率
        int totalUsed = 0, totalWaste = 0;
        List<Integer> remainingS = new ArrayList<>();
        for (List<Material> instances : materialMap.values()) {
            for (Material inst : instances) {
                totalUsed += inst.getLength() - inst.getRemaining();
                totalWaste += inst.getRemaining();
                remainingS.add(inst.getRemaining());
            }
        }
        fitness = (double) totalUsed / (totalUsed + totalWaste);

        remainingS.sort(Comparator.reverseOrder());

        double d = remainingS.get(0) + ((totalUsed + totalWaste) - remainingS.get(remainingS.size() - 1));
        while (d / 10 > 0.000001) {
            d /= 10;
        }
        fitness += d;
    }

    // 深度克隆方法（交叉操作依赖）
    @Override
    protected Individual clone() {
        Individual cloned = null;
        try {
            cloned = (Individual) super.clone();
        } catch (CloneNotSupportedException e) {
            throw new RuntimeException(e);
        }
        // 克隆引用类型的字段
        cloned.materialMap = new HashMap<>();
        for (Map.Entry<Integer, List<Material>> entry : materialMap.entrySet()) {
            cloned.materialMap.put(entry.getKey(), new ArrayList<>(entry.getValue()));
        }
        cloned.partsUsed = partsUsed.clone();
        cloned.partLengthToType = new HashMap<>(partLengthToType);
        cloned.materialLengthIndex = new HashMap<>(materialLengthIndex);
        cloned.partsLengthIndex = new HashMap<>(partsLengthIndex);
        cloned.materials = new ArrayList<>(materials);
        cloned.parts = new ArrayList<>(parts);
        // 如果 Material 类也需要深拷贝，需要递归调用其 clone 方法
        for (Map.Entry<Integer, List<Material>> entry : cloned.materialMap.entrySet()) {
            List<Material> clonedMaterials = new ArrayList<>();
            for (Material material : entry.getValue()) {
                clonedMaterials.add((Material) material.clone());
            }
            cloned.materialMap.put(entry.getKey(), clonedMaterials);
        }
        return cloned;
    }

    public Map<Integer, List<Material>> getMaterialMap() {
        return materialMap;
    }

    public void setMaterialMap(Map<Integer, List<Material>> materialMap) {
        this.materialMap = materialMap;
    }

    public Integer[] getPartsUsed() {
        return partsUsed;
    }

    public void setPartsUsed(Integer[] partsUsed) {
        this.partsUsed = partsUsed;
    }

    public double getFitness() {
        return fitness;
    }

    public void setFitness(double fitness) {
        this.fitness = fitness;
    }

    public boolean isValid() {
        return isValid;
    }

    public void setValid(boolean valid) {
        isValid = valid;
    }

    public Map<Integer, Integer> getPartLengthToType() {
        return partLengthToType;
    }

    public void setPartLengthToType(Map<Integer, Integer> partLengthToType) {
        this.partLengthToType = partLengthToType;
    }

    public Map<Integer, Integer> getMaterialLengthIndex() {
        return materialLengthIndex;
    }

    public void setMaterialLengthIndex(Map<Integer, Integer> materialLengthIndex) {
        this.materialLengthIndex = materialLengthIndex;
    }

    public Map<Integer, Integer> getPartsLengthIndex() {
        return partsLengthIndex;
    }

    public void setPartsLengthIndex(Map<Integer, Integer> partsLengthIndex) {
        this.partsLengthIndex = partsLengthIndex;
    }

    public List<MaterialReq> getMaterials() {
        return materials;
    }

    public void setMaterials(List<MaterialReq> materials) {
        this.materials = materials;
    }

    public List<PartsReq> getParts() {
        return parts;
    }

    public void setParts(List<PartsReq> parts) {
        this.parts = parts;
    }

    public Map<Integer, Integer> getPartCounts() {
        return partCounts;
    }

    public void setPartCounts(Map<Integer, Integer> partCounts) {
        this.partCounts = partCounts;
    }

    public Map<Integer, Integer> getMaterialCounts() {
        return materialCounts;
    }

    public void setMaterialCounts(Map<Integer, Integer> materialCounts) {
        this.materialCounts = materialCounts;
    }
}
